package com.cmy.init;

import com.cmy.bean.DdlHistory;
import com.cmy.bean.VersionComparator;
import com.cmy.constant.DDLConstant;
import com.cmy.dbTransfer.DBHandler;
import com.cmy.factory.DataBaseTransferFactory;
import com.cmy.service.DdlHistoryService;
import com.cmy.utils.ThrowableUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static com.cmy.constant.DDLConstant.DB_TYPE;

/**
 * @author : Cmy
 * @date : 2024-09-14 10:48
 */
@Slf4j
@Component
public class CustomDBStructurePostProcessor implements BeanPostProcessor {

    @Autowired
    private Environment env;

    @Autowired
    private DdlHistoryService ddlHistoryService;

    /**
     * 在数据源加载前
     *
     * 初始化数据库
     * 包含建库+ddl_history表结构
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof DataSource && "dataSource".equals(beanName)){
            log.info("-------------------------------------------------------------------");
            log.info("111111111111111111 - DataBase Initialize Start - 111111111111111111");
            initDataSource();
            log.info("111111111111111111 - DataBase Initialize End   - 111111111111111111");
            log.info("-------------------------------------------------------------------");
        }
        return bean;
    }

    /**
     * 在数据源加载后
     *
     * 初始化业务表结构
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof DataSource && "dataSource".equals(beanName)) {
            log.info("222222222222222222 - DataBase Tables Initialize Start - 222222222222222222");
            DataSource dataSource = (DataSource) bean;
            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
            initTablesConstruct(jdbcTemplate);
            log.info("222222222222222222 - DataBase Tables Initialize End   - 222222222222222222");
            log.info("--------------------------------------------------------------------------");
        }
        return bean;
    }

    private void initDataSource(){
        try {
            String property = env.getProperty("spring.auto-ddl.enabled");
            boolean ddlEnabled = property == null || Boolean.parseBoolean(property);

            if (ddlEnabled){

                log.info("enable ddl auto process");
                String name = env.getProperty("spring.datasource.name");

                //mysql jdbc:mysql://127.0.0.1:3306/dbName
                //dm jdbc:dm://10.10.160.70:5236?SCHEMA=${spring.datasource.name}
                //kingbase jdbc:kingbase8://10.10.140.31:54321${spring.datasource.name}?binaryTransferDisable=TINYINT
                String url = env.getProperty("spring.datasource.url");
                String urlWithoutName = url.replace("/"+name, "");
                String username = env.getProperty("spring.datasource.username");
                String password = env.getProperty("spring.datasource.password");

                //连接数据源建库
                try (Connection connection = DriverManager.getConnection(urlWithoutName, username, password)) {
                    DatabaseMetaData metaData = connection.getMetaData();
                    String databaseProductName = metaData.getDatabaseProductName();
                    log.info("dbname:{}",name);
                    DB_TYPE = databaseProductName;//设置数据库类型
                    log.info("DDLConstant.DB_TYPE:{}", DB_TYPE);
                    DBHandler dbHandler = DataBaseTransferFactory.getDBTransfer(DB_TYPE);
                    String createDataBaseSql = dbHandler.createDataBaseSql(name);
                    Statement statement = connection.createStatement();
                    boolean execute = statement.execute(createDataBaseSql);
                    log.info("create database status : {} ",!execute?"success":"failed");
                    if (!execute){//创建 ddl-history.sql 表
                        //连接数据源建表
                        try (Connection focusConnection = DriverManager.getConnection(url, username, password)) {
                            Statement focusStatement = focusConnection.createStatement();
                            String initTableDdlHistory = dbHandler.initTableDdlHistory();
                            boolean ddlHistoryTableInitStatus = focusStatement.execute(initTableDdlHistory);
                            log.info("table ddl_history init status : {} ",!ddlHistoryTableInitStatus?"success":"failed");
                        }
                    }

                } catch (Exception e) {
                    log.error(ThrowableUtil.getStackTrace(e));
                }
            }else {
                log.info("disable ddl auto process");
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    private void initTablesConstruct(JdbcTemplate jdbcTemplate){
        String property = env.getProperty("spring.auto-ddl.enabled");
        String username = env.getProperty("spring.datasource.username");
        boolean ddlEnabled = property==null || Boolean.parseBoolean(property);

        if (ddlEnabled){
            try {
                //这里执行sql
                Map<String, DdlHistory> ddlHistoryMap = new HashMap<>();
                //按照id升序
                List<DdlHistory> list = ddlHistoryService.cacheList(jdbcTemplate);
                if (!list.isEmpty()){
                    for (DdlHistory ddlHistory : list) {
                        String version = ddlHistory.getVersion();
                        if (!"R".equals(version)) {
                            //如果是其他的 key:version
                            ddlHistoryMap.put(version, ddlHistory);
                        }
                    }
                }

                Map<String, Resource> nameResourceMap = DDLConstant.nameResourceMap;
                List<Resource> orderedResourceList = nameResourceMap
                        .entrySet()
                        .stream()
                        .filter(entry->DDLConstant.VERSION_PATTERN_SQL.matcher(entry.getKey()).find())
                        .sorted((f1, f2) -> new VersionComparator().compare(f1.getKey(), f2.getKey()))
                        .map(Map.Entry::getValue)
                        .collect(Collectors.toList());

                for (Resource resource : orderedResourceList) {
                    log.info("======");
                    String filename = resource.getFilename();
                    boolean inSideJar = Objects.equals(resource.getURL().getProtocol(), "jar");
                    if (StringUtils.isBlank(filename)) {
                        continue;
                    }

                    String[] arr = filename.split("__");
                    if (arr.length<2){
                        continue;
                    }

                    String version = arr[0];
                    if (ddlHistoryMap.containsKey(version)) {
                        log.info("sql script [{}]:[{}] has already executed",inSideJar?"inside":"outside",filename);
                        log.info("======");
                    }else {
                        String description = arr[1];
                        DdlHistory ddlHistory = new DdlHistory();
                        ddlHistory.setVersion(version);
                        ddlHistory.setDescription(description);
                        ddlHistory.setScript(filename);
                        ddlHistory.setChecksum(-1L);
                        ddlHistory.setInstalledBy(username);
                        ddlHistory.setInstalledOn(LocalDateTime.now());
                        //执行然后 入ddl_history
                        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
                            StringBuilder sqlBuilder = new StringBuilder();
                            String line;
                            while ((line = reader.readLine()) != null) {
                                sqlBuilder.append(line).append("\n");
                            }
                            long start = System.currentTimeMillis();
                            jdbcTemplate.execute(sqlBuilder.toString());
                            long end = System.currentTimeMillis();
                            ddlHistory.setExecutionTime(end - start);
                            ddlHistory.setSuccess(true);
                            log.info("sql script [{}]:[{}] execute success ", inSideJar?"inside":"outside",description);
                        } catch (Exception e) {
                            log.error("sql script [{}]:[{}] execute fail :{}", inSideJar?"inside":"outside",description, e.getMessage());
                            ddlHistory.setExecutionTime(0L);
                            ddlHistory.setSuccess(false);
                        }
                        ddlHistoryService.save(ddlHistory,jdbcTemplate);
                        log.info("======");
                    }
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
    }





}

    